我手上現在有三張表,散在三篇文章裡。
Day 10 那張講速度跟準確率:純文字區、表格區、混排區各跑多快、各漏多少字。Day 11 那張講硬體:gpt-oss:20b 要 16GB,我三條路都走不通。昨天那張講供應鏈:誰的授權有地雷、誰的訓練資料是黑箱、誰直接出局。
三張表都沒錯,但我發現自己每次要做決定,都得在三個分頁之間切來切去。這很煩,而且容易漏看。今天想把它們疊成一個照著走就能下判斷的東西。
動手之前先釐清一件我昨晚想了很久的事。
硬體表回答「跑不跑得動」,供應鏈表回答「該不該用」。這兩個問題彼此獨立。一個模型可以跑得動但不該用,例如中國 origin 的模型,效能可能很漂亮,供應鏈那關就是過不去。也可以反過來,該用但跑不動,gpt-oss:20b 就是這樣,供應鏈乾淨,我這邊沒機器。
混在一起看會發生什麼?我自己差點掉進去:「反正 gpt-oss 也跑不動,供應鏈查了有什麼用。」
這個推論是錯的。硬體是會變的,下個月可能就借到一台機器。供應鏈的結論不會因為硬體到位就改變。如果今天不先把「該不該用」判完,等機器來了,我會因為急著跑起來而跳過審查。人在有機器可以玩的時候,判斷力是最差的。
所以順序是:先問不會變的,再問會變的。
按照這個原則,畫出來是五個節點。前兩個是硬門檻,一票否決;後三個是排序依據。
① 中國 origin?
是 → 排除。不進入任何比較。
依據:Day 12,國安局實測 Qwen 違規 11/15、DeepSeek 8/15,
美國、南韓、台灣多個機關 2025 年已有正式禁令。
否 → ②
② 這個角色需不需要跟系統內其他模型「不同源」?
需要(Verifier,見 Day 5 同源盲點、Day 11 獨立性原則)
→ 排除跟主模型同一家的候選。
主模型是 Google 的 Gemma-4,Verifier 只剩 gpt-oss / Nemotron。
不需要(OCR 主模型本身)→ ③
③ 現在跑得動嗎?(硬體+存取權限)
跑得動 → ④ 做正式效能比較
跑不動 → 先把介面訂好,狀態寫「等硬體」,不假裝已經在用
(gpt-oss:20b 目前就停在這裡,介面在 harness/verifier_protocol.py)
④ 效能夠不夠這個區塊用?
看 Day 10 的「有效產出 tok/s」跟覆蓋率,不看原始 tok/s。
純文字區:MoE 已經又快又準,不需要換。
表格區:問題是成本,換格式優先於換模型。
混排區:先做版面偵測,再談換模型。
⑤ 其他條件接近時,用這兩項排序:
訓練資料揭露:資料集清單 > 只給類別 > 不揭露
授權:標準 Apache 2.0 > Apache 2.0+usage policy > 自訂授權帶專利報復
跟我原本那版比,最大的改動是把效能放到第④步。
我原本的直覺是效能擺第一,那是工程師的本能。但照效能先排,第一名很可能就是一顆中國 origin 的模型(中文任務上它們確實強),然後我得在後面寫一大段「雖然它最快但我們不用」。這種寫法會讓人覺得供應鏈審查是事後找理由。硬門檻擺前面,被刷掉的東西根本不進入效能比較,邏輯乾淨很多。
Tip:決策樹的節點順序,本身就是一個價值判斷。把「隱私」放在「效能」前面,等於宣告效能再好也換不到隱私。如果你的場景不是這樣(例如只處理公開資料),順序就該調。不要照抄我的。
文字版的決策樹有一個問題:每個人讀起來都覺得自己懂了,但落到具體候選上,每個人的判斷又不一樣。
所以我把前三個節點寫成程式。這不是要自動化選型(選型一年做不到幾次),是要逼自己把「不同源」「跑得動」這些詞定義到電腦也看得懂的程度。
from dataclasses import dataclass, field
@dataclass
class Candidate:
name: str
vendor: str
china_origin: bool
runnable_now: bool
data_disclosure: int # 2=資料集清單 1=只給類別 0=不揭露
license_flags: list = field(default_factory=list)
def decide(c: Candidate, role: str, main_vendor: str = "Google") -> str:
if c.china_origin:
return "排除:中國 origin"
if role == "verifier" and c.vendor == main_vendor:
return "排除:與主模型同源"
if not c.runnable_now:
return "暫緩:先訂介面,等硬體"
note = f"可評測(揭露等級 {c.data_disclosure})"
if c.license_flags:
note += ",授權需法務看:" + "、".join(c.license_flags)
return note
pool = [
("ocr", Candidate("gemma-4-26b (MoE)", "Google", False, True, 1)),
("verifier", Candidate("gemma-4-26b (MoE)", "Google", False, True, 1)),
("verifier", Candidate("gpt-oss-20b", "OpenAI", False, False, 0)),
("verifier", Candidate("Nemotron-3-Nano-30B", "NVIDIA", False, False, 2, ["專利報復條款"])),
("verifier", Candidate("Qwen", "Alibaba", True, True, 1)),
]
for role, c in pool:
print(f"{role:8s} {c.name:22s} -> {decide(c, role)}")
跑出來:
ocr gemma-4-26b (MoE) -> 可評測(揭露等級 1)
verifier gemma-4-26b (MoE) -> 排除:與主模型同源
verifier gpt-oss-20b -> 暫緩:先訂介面,等硬體
verifier Nemotron-3-Nano-30B -> 暫緩:先訂介面,等硬體
verifier Qwen -> 排除:中國 origin
寫的過程中卡了兩次,這兩次卡住比程式本身有價值。
第一次卡在 vendor。「同源」到底是看公司、看訓練資料、還是看架構?如果某家新創拿 Gemma 的權重去 fine-tune,它算不算 Google 家?我最後選了最保守也最好查的定義:看原始權重的發布者。fine-tune 過的衍生模型,vendor 填原始發布者。這個定義不完美,但它至少可以被檢查。
第二次卡在 runnable_now。Nemotron 的欄位我一開始想填 True,因為「NVIDIA 的模型在 NVIDIA 的機器上總跑得動吧」。寫到一半發現自己連它的記憶體需求都沒查過(昨天的表上寫著未查證)。改成 False。一個布林值就把我的偷懶抓出來了。
注意 Nemotron 那一行,專利報復的提醒沒有印出來。因為它在第③步就停了,還沒走到授權那一段。這其實是對的:跑不動的模型,現在討論授權細節沒有意義。但這也提醒我,等它真的跑得動的那天,這條提醒會突然冒出來,到時候不能跳過。
這是今天真正要交出去的東西。把三張表的欄位合在一起,每個數字都標清楚是實測還是推算。
| 角色 | 候選 | 來源 | 總參數/活躍 | 量化與記憶體 | 速度 | 品質證據 | 資料揭露 | 授權 | 決策樹路徑 | 狀態 |
|---|---|---|---|---|---|---|---|---|---|---|
| OCR 主模型 | Gemma-4-26B MoE | 25.2B/3.8B | NVFP4 | 48.5 tok/s(實測,Day 3 長輸出收斂值) | 純文字覆蓋率 0.9941、表格 0.98 以上、混排 0.7619–0.8894(實測,Day 10) | 只給類別 | 標準 Apache 2.0 | ①否→③可跑→④ | 使用中 | |
| 表格區備選 | Gemma-4-31B Dense | 約 30.7B(沿用 Day 5 基數,一手規格未查證) | 假設 NVFP4,約 15.35GB(推算) | 天花板 17.8 tok/s,表格區有效產出約 7.5 tok/s(理論推算,未實測) | 無 | 只給類別 | 標準 Apache 2.0 | ①否→③未部署 | 候選席,優先度低 | |
| Verifier 首選 | gpt-oss-20b | OpenAI | 21B/3.6B | MXFP4,落地約 14GB,官方建議 16GB | GB10 理論上限約 152 tok/s(推算,未實測) | 無 | 不揭露 | Apache 2.0+usage policy | ①否→②不同源→③跑不動 | 介面已訂,等硬體 |
| Verifier 備選 | Nemotron-3-Nano-30B | NVIDIA | 未查證 | 未查證 | 無 | 無 | 約 141 個資料集 | 自訂授權,含專利報復、補償條款 | ①否→②不同源→③跑不動 | 供應鏈合格,效能未評估 |
| 直接排除 | Qwen、DeepSeek 等 | 中國 origin | 不比較 | 不比較 | 不比較 | 不比較 | 不比較 | 不比較 | ①是 | 排除 |
表很寬,手機上看大概要橫著滑。我考慮過拆成兩張,最後還是決定不拆。拆開之後就又回到「在分頁之間切來切去」的原點了,今天要解決的就是這件事。
看這張表時,我會先看「速度」跟「品質證據」兩欄裡有幾格寫著「無」或「推算」。答案是很多。整張表只有第一列是實測撐起來的,其他全部是紙上數字。
這張表誠實的地方在 Verifier 那兩列。我只能寫「等硬體」、「效能未評估」,不能寫「已選定 gpt-oss-20b」。決策樹不會幫我生出一台機器。它能做的是讓我在沒有機器的時候,先把不需要機器的判斷做完。等硬體到了,直接從第④步開始,不用重想一次前面三步。
表是我的,情境也是我的。拿一個跟我不一樣的情境試走一次,比較看得出這棵樹能不能搬走。
假設你是一家會計師事務所的工程師。要處理的是客戶還沒公告的財報底稿,機房裡有一張 24GB 的顯卡,老闆說資料一個 byte 都不能出去。(這是我編的範例情境,不是真實案例。)
走①:中國 origin 一律排除。這一步跟我完全一樣,而且你的理由比我更硬,因為你處理的是未公開資料。
走②:你要不要 Verifier?如果事務所的流程本來就有人工複核,你可能會覺得模型 Verifier 可有可無。我的建議是還是要。人工複核的人很容易被一份「看起來很乾淨」的 OCR 輸出說服,Verifier 的價值是在人看之前先把可疑的格子標出來。所以一樣排除同源的候選。
走③:24GB。gpt-oss-20b 官方寫 16GB 內可跑,MXFP4 落地約 14GB,理論上塞得進去,剩下的空間給 KV cache。注意這裡寫的是「理論上」,我自己沒在 24GB 的卡上試過。OCR 主模型那邊,26B MoE 用 NVFP4 的話權重大約是總參數的一半位元組,我這邊沒有在獨立顯卡上量過實際佔用,你得自己量。兩顆要同時常駐的話,24GB 大概不夠,得輪流載入。
走④:你的文件如果大部分是事務所自己排版的表格,Day 10 的結論(表格區問題是成本不是準確率)可能適用;如果是客戶傳來的掃描檔,我的數字完全不能參考,因為我測的全是原生 PDF。
走⑤:事務所有自己的法務,專利報復條款大概會被圈出來。Nemotron 在你這裡的排序可能比在我這裡更低。
走完一遍,你會發現有三個節點的答案跟我不同。這很正常。樹的結構可以搬,每個節點的答案得用你自己的資料填。
表格區那一列值得多講兩句,因為它的位置跟 Day 9 剛寫完時不一樣了。
Day 9 我說 31B Dense「沒被淘汰,放到候選席」。Day 10 算完之後,發現表格區覆蓋率已經 0.98 以上,換一顆慢 2.7 倍的模型去救那一兩個百分點,很不划算。真正爛的是混排區,但混排區的問題是沒有結構線索,換更大的模型也不會長出框線。
所以在決策樹上,31B Dense 其實卡在第④步之前就沒有進場的理由。它通過了①,通過了②(OCR 角色不需要不同源),停在③,因為我沒部署它。就算部署了,④也很可能判它不划算。
我對這個結果的感覺有點複雜。一方面數據很清楚;另一方面,我沒有真的跑過它,這整條推論是「推算疊推算」。我把它標成「優先度低」而不是「淘汰」,就是給自己留一個被打臉的空間。
最後講一個我還沒解決的問題。
這棵樹是從目前手上這幾個候選長出來的,不是先有通用框架再套進去的。所以它有一些節點,其實還沒被真正考驗過。
最明顯的是①跟②的關係。假設哪天出現一個供應鏈完全乾淨、效能也很好、偏偏又是 Google 家的新模型,想拿來當 Verifier。按照現在的樹,它在②就被刷掉了。但如果它的訓練資料跟 Gemma 完全不同呢?「同一家公司」就一定等於「同源盲點」嗎?
我不知道。Day 5 觀察到的同源盲點,是同一顆模型自己驗證自己。同公司的不同模型會不會有一樣的盲點,我沒有資料。現在的規則是寧可錯殺,因為 Verifier 的價值全靠獨立性,賭輸了整個驗證層就是裝飾品。但這個「寧可」是判斷,不是證據。
另一個弱點是④。它依賴 Day 10 的區塊分類,而 Day 10 只量了 5 頁、1 份法說會簡報。換成年報,版面的分布很可能完全不一樣。
這也是為什麼選型到這裡先收手。模型層該做的判斷做完了,剩下的要等真實文件來回答。
接下來四天換一條軸線:拿一份真的年報來跑。我已經下載好台積電 2025 年報的繁中版,92 頁,原生文字層。前面十三天的所有假設,會在那份 PDF 上被重新檢查一遍。